package mvc.core;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import mvc.core.annotation.Action;
import mvc.core.annotation.Batis;
import mvc.core.annotation.Impl;
import mvc.core.annotation.Inject;
import mvc.core.annotation.Mapping;
import mvc.core.annotation.Service;
import mvc.core.log.Logger;
import mvc.core.util.Util;

/**
 * Bean工厂
 * 
 * @author liuzy
 * @version 2015年5月13日
 */
public class BeanFactory {
	
	private Logger logger = Logger.getLogger(BeanFactory.class);
	
	public Set<Class<?>> allClasses = null;
	
    /** Bean仓库 */
    private final Map<Class<?>, Object> beanMap = new HashMap<Class<?>, Object>();
    
    /** Mapping仓库 */
    private final Map<String, ActionMapping> actionMappingMap = new HashMap<String, ActionMapping>();
    
    private static BeanFactory instance = new BeanFactory();
    
    private BeanFactory() {}

	public static BeanFactory getInstance() {
		return instance;
	}
 
    public void load() {
    	logger.debug("扫描加载Bean开始...");
        try {
        	allClasses = ClassScanner.scanner();
        	for (Class<?> cls : allClasses) {
    			if (cls.isAnnotationPresent(Action.class)) {
    				logger.debug("载入ActionBean:" + cls.getName());
    				beanMap.put(cls, (Object) cls.newInstance());
    				this.mappingAction(cls);
    			} else if (cls.isAnnotationPresent(Service.class)) {
    				logger.debug("载入ServiceBean:" + cls.getName());
    				beanMap.put(cls, (Object) cls.newInstance());
    			} else if (cls.isAnnotationPresent(Batis.class)) {
    				logger.debug("载入BatisBean:" + cls.getName());
    				beanMap.put(cls, (Object) new MapperProxyFactory<>(cls).newInstance());
    			}
    		}
        } catch (InstantiationException e1) {
            e1.printStackTrace();
        } catch (IllegalAccessException e2) {
            e2.printStackTrace();
        }
        logger.debug("扫描加载结束");
    }
    
    
    /**
     * 映射Action
     * @param cls 
     */
    private void mappingAction(Class<?> cls) {
    	String actionPath = "";
    	if (cls.isAnnotationPresent(Mapping.class)) {
    		actionPath += "/" + cls.getAnnotation(Mapping.class).value();
		}
    	Method[] methods = cls.getMethods();
		for (Method method : methods) {
			if (method.isAnnotationPresent(Mapping.class)) {
				String path = Util.reSlash(actionPath + "/" + method.getAnnotation(Mapping.class).value());
				actionMappingMap.put(path, new ActionMapping(path, cls, method));
				logger.debug("添加映射:path="+path+", action="+cls.getName()+", method="+method.getName());
			}
		}
    }
    
    /**
     * 注解注入属性
     */
    public void ioc() {
        logger.debug("注入Bean属性开始...");
		try {
			for (Map.Entry<Class<?>, Object> beanEntry : beanMap.entrySet()) {
				// 获取 Bean 类与 Bean 实例
				Class<?> beanClass = beanEntry.getKey();
				Object beanInstance = beanEntry.getValue();
				// 获取 Bean 类中所有的字段（不包括父类中的方法）
				Field[] beanFields = beanClass.getDeclaredFields();
				if (beanFields != null) {
					// 遍历所有的 Bean 字段
					for (Field beanField : beanFields) {
						// 判断当前 Bean 字段是否带有 @Inject 注解
						if (beanField.isAnnotationPresent(Inject.class)) {
							// 获取 Bean 字段对应的接口
							Class<?> interfaceClass = beanField.getType();
							// 判断接口上是否标注了 @Impl 注解
							Class<?> implClass = null;
							if (interfaceClass.isAnnotationPresent(Impl.class)) {
								// 获取强制指定的实现类
								implClass = interfaceClass.getAnnotation(Impl.class).value();
							} else if(interfaceClass.isAnnotationPresent(Service.class)) {
								implClass = interfaceClass;
							} else if(interfaceClass.isAnnotationPresent(Batis.class)) {
								implClass = interfaceClass;
							} else {
								// 寻找接口所有的实现类
								List<Class<?>> implClassList = new ArrayList<>();
								for (Class<?> cls : allClasses) {
					    			if (Util.isImpl(cls, interfaceClass))
					    				implClassList.add(cls);
					    			}
								if (implClassList.size() > 0) {
									// 获取第一个实现类
									implClass = implClassList.get(0);
								} else
									logger.error("注入"+beanClass.getName()+"的属性"+beanField.getName()+"失败,未找到实现类");
							}
							// 若存在实现类，则执行以下代码
							if (implClass != null) {
								// 从 Bean Map 中获取该实现类对应的实现类实例
								Object implInstance = beanMap.get(implClass);
								// 设置该 Bean 字段的值
								if (implInstance != null) {
									beanField.setAccessible(true); // 取消类型安全检测（可提高反射性能）
									beanField.set(beanInstance, implInstance);
									logger.debug("注入"+beanClass.getName()+"的属性"+beanField.getName()+"成功");
								}
							} else
								logger.error("注入"+beanClass.getName()+"的属性"+beanField.getName()+"失败");
						}
					}
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
        logger.debug("注入Bean属性结束");
	}
 
    @SuppressWarnings("unchecked")
    public <T> T getBean(Class<T> cls) {
        return (T) beanMap.get(cls);
    }
    
    public ActionMapping getActionMapping(String target) {
    	if (Util.isEmpty(target)) {
			return null;
		}
    	for (String key : actionMappingMap.keySet()) {
    		ActionMapping actionMapping = actionMappingMap.get(key);
			if (target.equals(actionMapping.getPath())) {
				return actionMapping;
			}
		}
    	return null;
    }

	public void destroy() {
		actionMappingMap.clear();
		beanMap.clear();
	}

}